package top.codechap.transaction;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;


/**
 * @author CodeChap
 * @date 2021-06-23 21:42
 * @description ConfigOfTransaction
 *
 * 声明式事务:
 *
 * 环境搭建:
 * 1. 导入相关依赖
 *      数据源, 数据库驱动, Spring-jdbc模块
 * 2. 配置数据源, JdbcTemplate(Spring提供的简化数据库操作的工具) 操作数据库
 * 3. 给Service层的具体实现方法上标注 @Transactional 表示当前的方法是一个事务方法
 * 4. 使用 EnableTransactionManagement 注解 ,开启基于注解的事务管理功能
 * 5.  配置事务管理器来管理事务
 *
 * 原理
 *      1) @EnableTransactionManagement
 *          利用 TransactionManagementConfigurationSelector 给容器中导入组件,
 *          @EnableTransactionManagement 中设置 adviceMode 的默认类型为 AdviceMode.PROXY, 利用 TransactionManagementConfigurationSelector 中的 selectImports()方法
 *          导入
 *          AutoProxyRegistrar
 *          和
 *          ProxyTransactionManagementConfiguration
 *          这两个组件
 *      2) AutoProxyRegistrar
 *          给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件
 *              InfrastructureAdvisorAutoProxyCreator 有何功能 ?
 *              利用后置处理器机制,在对象创建以后,包装对象,返回一个代理对象(增强器), 代理对象执行方法利用拦截器链进行调用
 *      3) ProxyTransactionManagementConfiguration 做了什么 ?
 *          (1) 给容器中注册事务增强器 (BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor())
 *              ① 事务增强器要用事务注解的信息(TransactionAttributeSource), 使用 AnnotationTransactionAttributeSource 解析事务注解
 *              ② 事务拦截器:
 *                  TransactionInterceptor: 保存了事务属性信息, 事务管理器; 在底层,它是一个MethodInterceptor(方法拦截器), 它会在目标方法执行的时候, 执行拦截器链;
 *                  在 事务拦截器 内的 invoke() 方法中, 执行 invokeWithinTransaction()方法:
 *                      a. 先获取事务相关的属性(invokeWithinTransaction)
 *                      b. 获取平台事务管理器PlatFormTransactionManager, 如果事先没有添加指定任何transactionManager, 最终会从容器中按照类型, 获取一个PlatFormTransactionManager
 *                      c. 执行目标方法
 *                          如果异常, 获取到事务管理器, 利用事务管理回滚操作;
 *                          如果正常, 利用事务管理器, 提交事务
 *
 *
 *
 */
@EnableTransactionManagement
@Configuration
@ComponentScan("top.codechap.transaction")
public class ConfigOfTransaction {

    //数据源
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("newpassword");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        return dataSource;
    }

    // Spring 对 @Configuration 类会做特殊处理
    // @Configuration 对象被cglib代理了,再次调用bean方法前,回显检查缓存中是否存在这个bean,因此不会再次创建
    @Bean
    public JdbcTemplate jdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); // 对于已经通过注解方式,创建并注入容器的组件,再次调用注入的方法时(例如这里),
                                                                    // 只会从容器中去寻找对应的组件,不会再次创建
        return jdbcTemplate;
    }

    // 注册事务管理器在容器中
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());  // 将数据源添加至事务管理器中,由事务管理器来
                                                                // 控制事务的执行,回滚等操作
    }
}
